/**
 * Wihidum - Efficient Broker Hierarchy for Publish Subscribe Middleware
 *
 * License      :   Apache License 2.0
 * Home Page    :   http://code.google.com/p/wihidum/
 * Mailing List :   wihidum@googlegroups.com
 */

package lk.mrt.cse.wihidum.client;

import lk.mrt.cse.wihidum.util.*;
import org.apache.axiom.om.*;
import org.apache.axiom.soap.*;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.*;
import org.apache.axis2.context.MessageContext;
import org.apache.log4j.Logger;

import java.util.UUID;

/**
 * This exposes methods for subscribers to send subscription related messages to the broker.
 * Subscribing web services or client applications can use this class for sending subscriptions.
 * Hence, this acts as an interface between the subscribing applications and the broker service.
 */
public class SubscriptionClient {
    private static final Logger logger = Logger.getLogger(SubscriptionClient.class);

    private SubscriptionClientBean clientbean;

    public SubscriptionClient(SubscriptionClientBean clientBean) {
        this.clientbean = clientBean;
    }

    public String subscribe() {
        String brokerAddress = clientbean.getBrokerAddress().getAddress();
        if (BrokerList.isEmpty()) {
            String adminAddress = clientbean.getAdminAddress().getAddress();
            String subscriberAddress = clientbean.getSubscriberAddress().getAddress();
            BrokerRetrievalClient brokerRetrievalClient = new BrokerRetrievalClient();
            brokerRetrievalClient.retrieveBrokers(adminAddress, subscriberAddress);

        }
        if (BrokerList.isEmpty()) {
            logger.info("Broker List is still empty. Therefore the broker address provide from the UI is taken.");
        } else {
            BrokerList.shuffleBrokerList();
            brokerAddress = BrokerList.getTopBroker();
            logger.info("Broker Address is taken from client's broker list. It is: " + brokerAddress);
        }

        try {
            //logger.info ("Sending subscription message");

            ServiceClient serviceClient = new ServiceClient();

            //creating in out operating client
            OperationClient opClient = serviceClient.createClient(ServiceClient.ANON_OUT_ONLY_OP);//ANON_OUT_IN_OP);

            //creating message context
            MessageContext outMsgCtx = new MessageContext();
            Options opts = outMsgCtx.getOptions();
            //setting properties into option
            //opts.setTo(clientbean.getBrokerAddress ());
            opts.setTo(new EndpointReference(brokerAddress));
            logger.info("Subscription request set to Subscrption Manager: " + brokerAddress);

            opts.setAction(WihidumConstants.Operations.SUBSCRIBE);
            opts.setFrom(clientbean.getSubscriberAddress());
            opts.setReplyTo(clientbean.getListenerAddress());

            //serviceClient.engageModule("addressing");
            String SOAPVersion = opts.getSoapVersionURI();
            if (SOAPVersion == null)
                SOAPVersion = SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI;
            SOAPEnvelope subscriptionEnvelope = createSubscriptionEnvelope(SOAPVersion);

            outMsgCtx.setEnvelope(subscriptionEnvelope);
            opClient.addMessageContext(outMsgCtx);
            opClient.execute(true);

            return "Subscription request sent to Subscrption Manager: " + brokerAddress;
        }
        catch (Exception e) {
            e.printStackTrace();
            return "Error in sending subscription";
        }

    }

    /**
     * Following method creates and returns a subscription request message according to the WS Eventing specification
     */
    private SOAPEnvelope createSubscriptionEnvelope(String SOAPURI) throws Exception {
        //generate the uuid of the subscription message using the java.util.UUID
        String uuid = UUID.randomUUID().toString();

        SOAPFactory factory = null;
        if (SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(SOAPURI))
            factory = OMAbstractFactory.getSOAP11Factory();
        else if (SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(SOAPURI))
            factory = OMAbstractFactory.getSOAP12Factory();
        else throw new Exception("Unknown SOAP version");
        SOAPEnvelope soapEnvelope = factory.getDefaultEnvelope();
        //declare the used namespaces in the SOAP Envelope
        OMNamespace wseventingNS = factory.createOMNamespace(WSEventingConstants.EVENTING_NAMESPACE, WSEventingConstants.EVENTING_PREFIX);
        soapEnvelope.declareNamespace(wseventingNS);
        OMNamespace wsaNS = factory.createOMNamespace(WSEventingConstants.ADDRESSING_NAMESPACE, WSEventingConstants.ADDRESSING_PREFIX);
        soapEnvelope.declareNamespace(wsaNS);
        OMNamespace wihidumNS = factory.createOMNamespace(WSEventingConstants.WIHIDUM_NAMESPACE, WSEventingConstants.WHIDUM_NAMESPACE_PREFIX);
        soapEnvelope.declareNamespace(wihidumNS);
        SOAPHeader soapHeader = soapEnvelope.getHeader();


        SOAPBody soapBody = soapEnvelope.getBody();

        //create the subscription element for the body
        OMElement subscriptionElement = factory.createOMElement(WSEventingConstants.ElementNames.Subscribe, wseventingNS);

        //Delivery Element
        OMElement deliveryElement = factory.createOMElement(WSEventingConstants.ElementNames.Delivery, wseventingNS);

        OMElement notifyToElement = factory.createOMElement(WSEventingConstants.ElementNames.NotifyTo, wseventingNS);

        //add delivery EPR as to where the notifications should be sent
        OMElement notifyToAddressElement = factory.createOMElement(WSEventingConstants.ElementNames.Address, wsaNS);
        notifyToAddressElement.setText(clientbean.getListenerAddress().getAddress());

        notifyToElement.addChild(notifyToAddressElement);

        //add clientID as the reference property
        OMElement referencePropertieElement = factory.createOMElement(WSEventingConstants.ElementNames.ReferenceProperties, wsaNS);

        OMElement subscriptionIDElement = factory.createOMElement(WSEventingConstants.ElementNames.SubscriptionID, wihidumNS);
        subscriptionIDElement.setText(clientbean.getSubscriptionID());

        referencePropertieElement.addChild(subscriptionIDElement);

        notifyToElement.addChild(referencePropertieElement);

        deliveryElement.addChild(notifyToElement);

        subscriptionElement.addChild(deliveryElement);

        //todo: define a format for filter dialact

        if (clientbean.getTopic() != null) {
            String filter = clientbean.getTopic();
            OMElement filterElement = factory.createOMElement(WSEventingConstants.ElementNames.Filter, wseventingNS);
            filterElement.setText(filter);

            subscriptionElement.addChild(filterElement);
        }

        soapBody.addChild(subscriptionElement);
        return soapEnvelope;
    }

}